package cn.seaboot.commons.thread;

import cn.seaboot.commons.core.Asserts;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Semaphore;

/**
 * 业务场景：我们有一些图片，可以允许任何人修改，但是任何时候只能有一个人进行修改，
 * 在其他人进行修改的时候，你希望当前线程是进行等待，而不是直接退出。
 * <p>
 * 一般用于作业调度，避免作业并发执行，产生不可预知的问题。
 * <p>
 * 这个工具类基于信号量实现，可以给任何你所希望的事物进行加锁。
 *
 * @author Mr.css
 * @version 2022-04-02 14:51
 * @see Semaphore
 */

public class SingletonLock {

    /**
     * 无所谓竞争，用普通的 map 即可，写法上保证了任何时候只会有一个线程能够成功执行
     */
    private final Map<String, Semaphore> semaphoreMap = new HashMap<>();

    /**
     * 预备一个信号量，为了避免构造同名的信号量，这个函数是加锁的
     *
     * @param name name of locked object
     * @return 信号量
     */
    private Semaphore prepareSemaphore(String name) {
        Semaphore semaphore = semaphoreMap.get(name);
        if (semaphore == null) {
            synchronized (SingletonLock.class) {
                semaphore = semaphoreMap.computeIfAbsent(name, k -> new Semaphore(1));
            }
        }
        return semaphore;

    }

    /**
     * 加锁
     *
     * @param name name of locked object
     * @throws InterruptedException if the current thread is interrupted
     */
    public void lock(String name) throws InterruptedException {
        Semaphore semaphore = this.prepareSemaphore(name);
        semaphore.acquire();
    }

    /**
     * 释放锁
     *
     * @param name name of locked object
     */
    public void release(String name) {
        Semaphore semaphore = semaphoreMap.get(name);
        Asserts.notNull(semaphore, "semaphore not found:" + name);
        semaphore.release();
    }
}
